/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\mimicinfopack.h"

#include <stdlib.h> // for _byteswap_ushort

//---------------------------------------------------------------------------

MimicInfoPack::MimicInfoPack(void)
{
	mimicOS = __mosRandom;

	rnd.SeedMT(GetTickCount());

	model.Initialize();

	sharedVal = 0;

	firstValue = true;
}

MimicInfoPack::~MimicInfoPack(void)
{
}

//---------------------------------------------------------------------------

void MimicInfoPack::Set(MimicOS typeIn)
{
	if(mimicOS!=__mosRandom){
		if(mimicOS==typeIn){
			// Set has been called so continue, do NOT initialize again
			return;
		}
	}

	mimicOS = typeIn;

	sharedVal = rnd.GetWordValue();

	model.Set(typeIn);

	Initialize();

	firstValue = true;
}

//---------------------------------------------------------------------------

MimicOS MimicInfoPack::GetMimic(void)
{
	return mimicOS;
}

//---------------------------------------------------------------------------

WORD MimicInfoPack::InitWordValue(WORD valIn)
{
	WORD val;

	switch(valIn){
		case MIMIC_INC_TYPE_RS1 :
			val = sharedVal;
			break;

		case MIMIC_INC_TYPE_R :
		case MIMIC_INC_TYPE_R1 :
		case MIMIC_INC_TYPE_R1S :
		case MIMIC_INC_TYPE_R3S :
		case MIMIC_INC_TYPE_R5 :
			val = rnd.GetWordValue();
			break;

		default :
			///< static value
			val = valIn;
			break;
	}

	return val;
}

WORD MimicInfoPack::IncrWordValue(WORD typeIn, WORD *valInPtr)
{
	WORD valIn;
	WORD val;

	if(valInPtr==NULL) return rnd.GetWordValue();

	valIn = *valInPtr;

	switch(typeIn){
		case MIMIC_INC_TYPE_R :
			///< random
			val = rnd.GetWordValue();
			while(!WordValueIsInLimits(val))
				val = rnd.GetWordValue();
			*valInPtr = val;
			break;

		case MIMIC_INC_TYPE_RS1 :
			///< random, then incremented by 1 and shared (IPID)
			sharedVal++;
			if(!WordValueIsInLimits(sharedVal))
				sharedVal = MIMIC_INC_MIN_VAL;
			val = sharedVal;
			*valInPtr = val;
			break;

		case MIMIC_INC_TYPE_R1 :
			///< random, then incremented by 1
			val = valIn + 1;
			if(!WordValueIsInLimits(val))
				val = MIMIC_INC_MIN_VAL;
			*valInPtr = val;
			break;

		case MIMIC_INC_TYPE_R1S :
			///< random, then incremented by 1 and swapped
			val = _byteswap_ushort(valIn);
			val = val + 1;
			if(!WordValueIsInLimits(val))
				val = MIMIC_INC_MIN_VAL;
			val = _byteswap_ushort(val);
			*valInPtr = val;
			break;

		case MIMIC_INC_TYPE_R3S :
			///< random, then incremented by 3 and swapped
			val = _byteswap_ushort(valIn);
			val = val + 3;
			if(!WordValueIsInLimits(val))
				val = MIMIC_INC_MIN_VAL;
			val = _byteswap_ushort(val);
			*valInPtr = val;
			break;

		case MIMIC_INC_TYPE_R5 :
			///< random, then incremented by 5
			val = valIn + 5;
			if(!WordValueIsInLimits(val))
				val = MIMIC_INC_MIN_VAL;
			*valInPtr = val;
			break;

		default :
			///< static value
			val = valIn;
			*valInPtr = val;
			break;
	}

	return val;
}

bool MimicInfoPack::WordValueIsInLimits(WORD wIn)
{
	if(wIn<MIMIC_INC_MIN_VAL) return false;
	if(wIn>MIMIC_INC_MAX_VAL) return false;

	return true;
}

//---------------------------------------------------------------------------

WORD MimicInfoPack::GetRandomPort(void)
{
	WORD val;

	val = rnd.GetWordValue();

	if(model.srcPort>MIMIC_INC_MAX_VAL){
		///< No minimum source port specified, go on with the generated random value unless it is zero
		while(val==0){
			rnd.SeedMT(GetTickCount());
			val = rnd.GetWordValue();
		}
		return val;
	}

	/*!
		This approach returns a result with only a few additions and I think it is faster that 
		generating a new random number.
	*/
	while(val < model.srcPort)
		val += model.srcPort;

	return val;
}

WORD MimicInfoPack::GetIncrPort(void)
{
	WORD val;

	val = value.srcPort + 1;

	if(val==0){
		// overflow

		if(model.srcPort>MIMIC_INC_MAX_VAL){
			///< No minimum source port specified, go on with 0x0404
			val = 0x0404;
		}
		else{
			val = model.srcPort;
		}
	}

	return val;
}

void MimicInfoPack::ComputeSourcePort(bool initializeIn)
{
	if(initializeIn){
		value.srcPort = GetRandomPort();
		return;
	}

	if(mimicOS==__mosRandom) value.srcPort = GetRandomPort();
	else                     value.srcPort = GetIncrPort();
}

//---------------------------------------------------------------------------

void MimicInfoPack::Initialize(void)
{
	value.LoadFromPointer(&model);

	value.wIpIdIcmp    = InitWordValue(model.wIpIdIcmp);
	value.wIpIdTcp     = InitWordValue(model.wIpIdTcp);
	value.wIpIdUdp     = InitWordValue(model.wIpIdUdp);

	value.wFragOffIcmp = InitWordValue(model.wFragOffIcmp);
	value.wFragOffTcp  = InitWordValue(model.wFragOffTcp);
	value.wFragOffUdp  = InitWordValue(model.wFragOffUdp);

	value.wIcmpId      = InitWordValue(model.wIcmpId);
	value.wIcmpSeq     = InitWordValue(model.wIcmpSeq);

	value.wTransId     = InitWordValue(model.wTransId);

	value.wIcmpId6     = InitWordValue(model.wIcmpId6);
	value.wIcmpSeq6    = InitWordValue(model.wIcmpSeq6);

	ComputeSourcePort(true);
}

//---------------------------------------------------------------------------

void MimicInfoPack::GetNewSet(MimicInfo* mi, MimicInfoIpProtocol miip)
{
	if(mi==NULL) return;

	mi->LoadFromPointer(&value);

	if(firstValue){
		firstValue = false;
		return;
	}

	switch(miip){
		case __miipIcmp :
			mi->wIpIdIcmp    = IncrWordValue(model.wIpIdIcmp,    &value.wIpIdIcmp);
			mi->wFragOffIcmp = IncrWordValue(model.wFragOffIcmp, &value.wFragOffIcmp);

			mi->wIcmpId      = IncrWordValue(model.wIcmpId,      &value.wIcmpId);
			mi->wIcmpSeq     = IncrWordValue(model.wIcmpSeq,     &value.wIcmpSeq);
			break;

		case __miipTcp :
			mi->wIpIdTcp     = IncrWordValue(model.wIpIdTcp,     &value.wIpIdTcp);
			mi->wFragOffTcp  = IncrWordValue(model.wFragOffTcp,  &value.wFragOffTcp);

			ComputeSourcePort(false);
			mi->srcPort = value.srcPort;
			break;

		case __miipUdp :
			mi->wIpIdUdp     = IncrWordValue(model.wIpIdUdp,     &value.wIpIdUdp);
			mi->wFragOffUdp  = IncrWordValue(model.wFragOffUdp,  &value.wFragOffUdp);

			mi->wTransId     = IncrWordValue(model.wTransId,     &value.wTransId);

			ComputeSourcePort(false);
			mi->srcPort = value.srcPort;
			break;

		case __miipIgmp :
			mi->bMaxResponseTime = value.bMaxResponseTime;
			break;
	}
}

//---------------------------------------------------------------------------

void MimicInfoPack::GetNewSet6(MimicInfo* mi, MimicInfoIpProtocol miip)
{
	if(mi==NULL) return;

	mi->LoadFromPointer(&value);

	if(firstValue){
		firstValue = false;
		return;
	}

	switch(miip){
		case __miipIcmp :
			mi->wIcmpId6  = IncrWordValue(model.wIcmpId6,    &value.wIcmpId6);
			mi->wIcmpSeq6 = IncrWordValue(model.wIcmpSeq6,   &value.wIcmpSeq6);
			break;

		case __miipTcp :
			ComputeSourcePort(false);
			mi->srcPort = value.srcPort;
			break;

		case __miipUdp :
			ComputeSourcePort(false);
			mi->srcPort = value.srcPort;
			break;

		case __miipIgmp :
			mi->bMaxResponseTime = value.bMaxResponseTime;
			break;
	}
}

//---------------------------------------------------------------------------

BYTE MimicInfoPack::GetIgmpMaxResponseTime(void)
{
	return value.bMaxResponseTime;
}

//---------------------------------------------------------------------------
